import uicls
import uiutil
import uiconst
import uthread
import stackless
import blue
import types
import util
import base
import log
import uiconstLoaded
CLICK = uiconst.UI_CLICK
DBLCLICK = uiconst.UI_DBLCLICK
TRIPLECLICK = uiconst.UI_TRIPLECLICK
DESTROY = uiconst.UI_DESTROY
MOUSEDOWN = uiconst.UI_MOUSEDOWN
MOUSEUP = uiconst.UI_MOUSEUP
PICKSTATES = (uiconst.UI_NORMAL,
 uiconst.UI_PICKCHILDREN,
 uiconst.UI_DISABLED)
UTHREADEVENTS = (CLICK,
 DBLCLICK,
 TRIPLECLICK,
 uiconst.UI_KEYUP,
 uiconst.UI_KEYDOWN)
CURSORATTR = 'cursor'
HINTATTR = 'hint'
STATEATTR = 'state'
SRATTR = 'sr'
WRATTR = 'wr'
CLICKCOUNTRESETTIME = 250
CHECKATTACHMENTS = 'left_top_width_height__align'
TUPLETYPE = type(())

def BuildDocString(cls):
    classHierarchy = ''
    chIndent = '  '
    parentDict = {}
    classTree = cls.__mro__
    total = len(classTree)
    for i in xrange(total):
        anchestor = classTree[((total - i) - 1)]
        guid = anchestor.__dict__.get('__guid__', None)
        if guid:
            parentDict.update(anchestor.__dict__)
            classHierarchy += ('%s%s\n' % (chIndent, guid))
            chIndent += '   '

    displayAttributes = []
    for (k, v,) in parentDict.iteritems():
        if k.startswith('default_'):
            displayAttributes.append((k[8:], v))

    displayAttributes.sort()
    if cls.__doc__:
        ret = (cls.__doc__ + '\n')
    else:
        ret = ''
    maxWidth = 0
    lines = []
    for (attributeName, attributeValue,) in displayAttributes:
        attributeValue = GetDisplayAttribute(attributeName, attributeValue)
        line = ('  %s: %s' % (attributeName, attributeValue))
        maxWidth = max(maxWidth, len(line))
        lines.append((line, attributeName))

    glob = globals()
    lineformat = ('%%-%ss # %%%ss\n' % ((maxWidth + 1), ''))
    lineformatXtra = ('%%-%ss   %%%ss\n' % ((maxWidth + 1), ''))
    ret += 'Default attributes:\n'
    for (line, attributeName,) in lines:
        docName = ('%s_doc' % attributeName)
        if hasattr(cls, docName):
            docStr = getattr(cls, docName)
        else:
            docStr = glob.get(('default_%s' % docName), '-')
        hintString = docStr.split('\n')
        ret += (lineformat % (line, hintString[0]))
        for part in hintString[1:]:
            ret += (lineformatXtra % ('', part))


    ret += ('\nClass Hierarchy:\n' + classHierarchy)
    return ret


default_clipChildren_doc = "Childrens will be clipped by this item's boundingbox if True"
default_pickRadius_doc = 'Sets pickradius to this (pixel) value. -1 will result in oval picking \nno matter what the size of the container is'
default_opacity_doc = 'Sets opacity of this container, 0.0 is transparent, 1.0 fully opaque'
default_name_doc = 'Sets the name of this item'
default_align_doc = 'Sets align of this item, see uicls._BaseDecoCore.SetAlign for details'
default_state_doc = 'Sets state of this item'
default_pos_doc = 'Sets position and size of this item (left, top, width, height) (which can also be set individually)'
default_idx_doc = 'Defines the location (index) where this item will added, 0 is at top, -1 is at bottom'
default_padding_doc = 'Sets the padding (padLeft,padTop,padRight,padBottom) (which can also be set individually)'
default_parent_doc = 'Defines the parent this item will be added into'
default_color_doc = 'Set color of this sprite, (r,g,b,a)'
default_hint_doc = 'Sets (MouseOver) hint on the item. Note: that the state has to be UI_NORMAL'

def GetDisplayAttribute(attributeName, attributeValue):

    def FormatState(attributeValue):
        for stateName in ('UI_NORMAL', 'UI_DISABLED', 'UI_HIDDEN', 'UI_PICKCHILDREN'):
            stateConst = getattr(uiconst, stateName, None)
            if (stateConst == attributeValue):
                return ('uiconst.' + stateName)

        return attributeValue



    def FormatAlign(attributeValue):
        for stateName in ('ABSOLUTE', 'RELATIVE', 'TOPLEFT', 'TOPRIGHT', 'BOTTOMLEFT', 'BOTTOMRIGHT', 'TOLEFT', 'TOTOP', 'TOBOTTOM', 'TORIGHT', 'TOALL', 'CENTERLEFT', 'CENTERRIGHT', 'CENTERTOP', 'CENTERBOTTOM', 'CENTER'):
            stateConst = getattr(uiconst, stateName, None)
            if (stateConst == attributeValue):
                return ('uiconst.' + stateName)

        return attributeValue


    if (attributeName == 'state'):
        attributeValue = FormatState(attributeValue)
    elif (attributeName == 'align'):
        attributeValue = FormatAlign(attributeValue)
    elif (type(attributeValue) in types.StringTypes):
        attributeValue = (('"' + attributeValue) + '"')
    return attributeValue



class DecoMetaclass(type):

    def __new__(mcs, name, bases, dict):
        cls = type.__new__(mcs, name, bases, dict)
        cls.__persistvars__ = cls._CombineLists('__persistvars__')
        cls.__nonpersistvars__ = cls._CombineLists('__nonpersistvars__')
        cls.__doc__ = BuildDocString(cls)
        return cls



    def __call__(_cls, _raw = None, _initDict = None, **kw):
        if not _raw:
            resPath = getattr(_cls, '__resPath__', None)
            if resPath:
                inst = blue.os.LoadObject(resPath)
            else:
                inst = blue.os.CreateInstance(_cls.__cid__)
        else:
            inst = _raw
            kw['__gotShape__'] = True
        inst.__klass__ = _cls
        if (_initDict is not None):
            for (k, v,) in _initDict.iteritems():
                setattr(inst, k, v)

        elif hasattr(inst, '_Initialize'):
            inst._Initialize(**kw)
        else:
            inst.__init__(**kw)
        return inst



    def _CombineLists(cls, name):
        result = []
        for b in cls.__mro__:
            if hasattr(b, name):
                for k in getattr(b, name):
                    if (k not in result):
                        result.append(k)


        return result




def GetDecoMetaclassInst(cid):

    class DecoMetaclassInst(object):
        __metaclass__ = DecoMetaclass
        __cid__ = cid

    return DecoMetaclassInst



class _BaseDecoCore(object):
    __guid__ = 'uicls._BaseDecoCore'
    __persistvars__ = ['_clicks',
     '_visibleState',
     '_setalign',
     '_preDisableState',
     '_clickedOtherButton']
    __nonpersistvars__ = ()
    eventNames = ('MouseDown', 'MouseExit', 'MouseHover', 'MouseUp', 'MouseMove', 'MouseEnter', 'MouseWheel', 'KeyDown', 'KeyUp', 'Click', 'StateChange', 'SetParent', 'Resize', 'Close', 'Destroy')
    eventMap = {}
    for funcName in eventNames:
        eventConst = getattr(uiconst, ('UI_%s' % funcName.upper()))
        exec ('def Do%s(self, *args):                     self.ProcessEvent(%s, *args)' % (funcName, eventConst))
        eventMap[eventConst] = ('On%s' % funcName)

    del funcName
    del eventConst
    del eventNames
    default_name = 'basedecocore'
    default_parent = None
    default_idx = -1
    default_state = uiconst.UI_NORMAL
    default_align = uiconst.TOPLEFT
    default_hint = None
    default_pos = None
    default_left = 0
    default_top = 0
    default_width = 0
    default_height = 0
    default_padding = None
    default_padLeft = 0
    default_padTop = 0
    default_padRight = 0
    default_padBottom = 0

    def _Initialize(self, *args, **kw):
        self._clicks = 0
        self._visibleState = None
        self._setalign = None
        self._preDisableState = None
        self._clickedOtherButton = False
        self._attachedObjects = []
        self._attachedTo = None
        attributesBunch = uiutil.Bunch(kw)
        self.ApplyAttributes(attributesBunch)
        if self.destroyed:
            return 
        self.PostApplyAttributes(attributesBunch)
        if self.destroyed:
            return 
        self.init()



    def init(self):
        return 



    def LoadBlueFile(self, filename):
        for each in self.children[:]:
            each.Close()

        for each in eve.rot.GetCopy(filename).children[:]:
            uiutil.Transplant(each, self)




    def ApplyAttributes(self, attributes):
        if attributes.__gotShape__:
            return 
        self.name = attributes.get('name', self.default_name)
        pos = attributes.get('pos', self.default_pos)
        if (pos is not None):
            (left, top, width, height,) = pos
        else:
            (left, top, width, height,) = (0, 0, 0, 0)
        if ('left' in attributes):
            left = attributes.left
        if ('top' in attributes):
            top = attributes.top
        if ('width' in attributes):
            width = attributes.width
        if ('height' in attributes):
            height = attributes.height
        self.SetSize(width, height)
        self.SetPosition(left, top)
        padding = attributes.get('padding', self.default_padding)
        if (padding is not None):
            (padLeft, padTop, padRight, padBottom,) = padding
        else:
            (padLeft, padTop, padRight, padBottom,) = (0, 0, 0, 0)
        padLeft = (attributes.get('padLeft', self.default_padLeft) or padLeft)
        padTop = (attributes.get('padTop', self.default_padTop) or padTop)
        padRight = (attributes.get('padRight', self.default_padRight) or padRight)
        padBottom = (attributes.get('padBottom', self.default_padBottom) or padBottom)
        self.SetPadding(padLeft, padTop, padRight, padBottom)
        self.SetState(attributes.get('state', self.default_state))
        self.SetAlign(attributes.get('align', self.default_align))
        hint = attributes.get('hint', self.default_hint)
        if (hint is not None):
            self.SetHint(hint)
        lockAspect = attributes.get('lockAspect', 0)
        if (lockAspect and (self.GetAlign() in (uiconst.TOTOP,
         uiconst.TOLEFT,
         uiconst.TOBOTTOM,
         uiconst.TORIGHT))):
            self.SetAspectLock(lockAspect)
        parent = attributes.get('parent', self.default_parent)
        if (parent is not None):
            if not hasattr(parent, 'children'):
                return self
            idx = attributes.get('idx', None)
            if (idx is None):
                idx = self.default_idx
            if (idx in (-1, None)):
                parent.children.append(self)
            else:
                parent.children.insert(idx, self)
        for each in ('func', 'args', 'hint', 'GetMenu', 'btn_modalresult', 'btn_default', 'btn_cancel', 'solid'):
            if (each in attributes):
                attr = attributes.get(each, None)
                setattr(self, each, attr)




    def PostApplyAttributes(self, attributes):
        return 



    def __repr__(self):
        return ('UIObject: guid=%s name=%r destroyed=%s' % (self.__guid__,
         self.name,
         self.destroyed))



    def __getattr__(self, k):
        if (k in self.__dict__):
            return self.__dict__[k]
        if ((k == SRATTR) and (k not in self.__dict__)):
            self.sr = sr = uiutil.Bunch()
            return sr
        if ((k == WRATTR) and (k not in self.__dict__)):
            self.wr = wr = util.WeakRefAttrObject()
            return wr
        raise AttributeError, k



    def __setattr__(self, k, v):
        if self.dead:
            return 
        blueAttr = ('_parent_' + k)
        if hasattr(self, blueAttr):
            setattr(self, blueAttr, v)
        else:
            self.__dict__[k] = v



    def _OnClose(self, *args, **kw):
        attachedTo = getattr(self, '_attachedTo', None)
        if attachedTo:
            object = attachedTo[0]
            object._RemoveAttachment(self)
        notifyevents = getattr(self, '__notifyevents__', None)
        if notifyevents:
            sm.UnregisterNotify(self)



    def _FindHandler(self, map):
        method = getattr(self, map, None)
        if method:
            return (True, method)
        else:
            method = getattr(self, ('_' + map), None)
            if method:
                return (False, method)
            return (True, None)



    def _ResetClickCounter(self, *args):
        self.sr.clicktimer = None
        self._clicks = 0



    def KillClickThreads(self):
        self.sr.clicktimer = None



    def ProcessEvent(self, eventID, *args):
        if ((eventID != uiconst.UI_CLOSE) and self.destroyed):
            return 
        mapped = self.eventMap[eventID]
        (needsSelf, Handler,) = self._FindHandler(mapped)
        if (eventID == CLICK):
            if (uicore.uilib.mouseOver != self):
                return 
            self.sr.clicktimer = None
            self._clicks += 1
            if (self._clicks == 1):
                self._ExecuteHandler(CLICK, Handler, args, needsSelf)
            (dblNeedsSelf, dblHandler,) = self._FindHandler('OnDblClick')
            (tripleNeedsSelf, tripleHandler,) = self._FindHandler('OnTripleClick')
            if (dblHandler or tripleHandler):
                if (dblHandler and (self._clicks == 2)):
                    self._ExecuteHandler(DBLCLICK, dblHandler, args, dblNeedsSelf)
                else:
                    if (tripleHandler and (self._clicks == 3)):
                        self._ExecuteHandler(TRIPLECLICK, tripleHandler, args, tripleNeedsSelf)
            else:
                self._clicks = 0
            self.sr.clicktimer = base.AutoTimer(CLICKCOUNTRESETTIME, self._ResetClickCounter)
            return 
        if (eventID == MOUSEDOWN):
            (btn,) = args
            if (((btn == uiconst.MOUSELEFT) or uicore.uilib.leftbtn) and (not getattr(self, 'expandOnLeft', 0))):
                self._clickedOtherButton = True
            else:
                self._clickedOtherButton = False
        elif ((eventID == MOUSEUP) and ((getattr(self, 'GetMenu', None) is not None) and ((not self._clickedOtherButton) and (uicore.uilib.mouseOver == self)))):
            (btn,) = args
            if (((btn == uiconst.MOUSERIGHT) and (not uicore.uilib.leftbtn)) or ((btn == uiconst.MOUSELEFT) and (getattr(self, 'expandOnLeft', 0) and (not uicore.uilib.rightbtn)))):
                import menu
                uthread.new(menu.ShowMenu, self)
        self._ExecuteHandler(eventID, Handler, args, needsSelf)


    SendMessage = ProcessEvent

    def _ExecuteHandler(self, eventID, Handler, args, needsSelf):
        if (type(Handler) == TUPLETYPE):
            customArgs = Handler[1:]
            Handler = Handler[0]
        else:
            customArgs = ()
        if Handler:
            if not needsSelf:
                args = (customArgs + args)
            else:
                args = ((customArgs + (self)) + args)
            if (eventID in UTHREADEVENTS):
                uthread.new(Handler, *args)
            else:
                Handler(*args)
        if (eventID == uiconst.UI_CLOSE):
            self.sr = None
            self.wr = None
            del self.__klass__



    def OnSetAttr(self, k, v):
        if (k == STATEATTR):
            if (v in PICKSTATES):
                self._visibleState = v
        elif (k == CURSORATTR):
            if (uicore.uilib.mouseOver == self):
                uicore.CheckCursor()
        elif (k == HINTATTR):
            if (uicore.uilib.mouseOver == self):
                uicore.CheckHint()
        elif (k in CHECKATTACHMENTS):
            self._CheckAttachments()



    def SetHint(self, hint):
        self.hint = (hint or '')



    def GetHint(self):
        return getattr(self, HINTATTR, None)



    def SetPadding(self, padLeft = None, padTop = None, padRight = None, padBottom = None):
        if (padLeft is not None):
            self.padLeft = padLeft
        if (padTop is not None):
            self.padTop = padTop
        if (padRight is not None):
            self.padRight = padRight
        if (padBottom is not None):
            self.padBottom = padBottom



    def GetPadding(self):
        return (self.padLeft,
         self.padTop,
         self.padRight,
         self.padBottom)



    def SetPosition(self, left, top):
        self.left = left
        self.top = top



    def GetPosition(self):
        return (self.left, self.top)



    def SetSize(self, width, height):
        self.width = width
        self.height = height



    def GetSize(self):
        return (self.width, self.height)



    def SetState(self, state):
        self.state = state



    def GetState(self):
        return self.state



    def _OnResize(self):
        self._CheckAttachments()



    def _CheckAttachments(self):
        if hasattr(self, '_attachedObjects'):
            attachedObjects = self._attachedObjects
            if attachedObjects:
                for object in attachedObjects:
                    object.UpdateAttachedPosition()




    def _RemoveAttachment(self, attachment):
        if (attachment in self._attachedObjects):
            self._attachedObjects.remove(attachment)



    def _AddAttachment(self, attachment):
        if (attachment not in self._attachedObjects):
            self._attachedObjects.append(attachment)



    def SetAttachTo(self, toObject, objectPoint, myPoint, offset = None):
        if self._attachedTo:
            oldObject = self._attachedTo[0]
            oldObject._RemoveAttachment(self)
            self._attachedTo = None
        if (toObject.parent != self.parent):
            raise RuntimeError, 'SetAttachTo: toObject has to be on same level as the attachment'
        self.SetAlign(uiconst.RELATIVE)
        self._attachedTo = (toObject,
         objectPoint,
         myPoint,
         offset)
        toObject._AddAttachment(self)
        self.UpdateAttachedPosition()



    def UpdateAttachedPosition(self):
        if self._attachedTo:
            (toObject, objectPoint, myPoint, offset,) = self._attachedTo
            (al, at, aw, ah,) = toObject.GetAbsolute()
            (pl, pt, pw, ph,) = self.parent.GetAbsolute()
            if (objectPoint in (uiconst.ANCH_TOPLEFT,
             uiconst.ANCH_CENTERTOP,
             uiconst.ANCH_TOPRIGHT)):
                yPoint = (at - pt)
            elif (objectPoint in (uiconst.ANCH_CENTERLEFT,
             uiconst.ANCH_CENTER,
             uiconst.ANCH_CENTERRIGHT)):
                yPoint = ((at - pt) + (ah / 2))
            elif (objectPoint in (uiconst.ANCH_BOTTOMLEFT,
             uiconst.ANCH_CENTERBOTTOM,
             uiconst.ANCH_BOTTOMRIGHT)):
                yPoint = ((at - pt) + ah)
            if (myPoint in (uiconst.ANCH_TOPLEFT,
             uiconst.ANCH_CENTERTOP,
             uiconst.ANCH_TOPRIGHT)):
                self.top = yPoint
            elif (myPoint in (uiconst.ANCH_CENTERLEFT,
             uiconst.ANCH_CENTER,
             uiconst.ANCH_CENTERRIGHT)):
                self.top = (yPoint - (self.height / 2))
            elif (myPoint in (uiconst.ANCH_BOTTOMLEFT,
             uiconst.ANCH_CENTERBOTTOM,
             uiconst.ANCH_BOTTOMRIGHT)):
                self.top = (yPoint - self.height)
            if (objectPoint in (uiconst.ANCH_TOPLEFT,
             uiconst.ANCH_CENTERLEFT,
             uiconst.ANCH_BOTTOMLEFT)):
                xPoint = (al - pl)
            elif (objectPoint in (uiconst.ANCH_CENTERTOP,
             uiconst.ANCH_CENTER,
             uiconst.ANCH_CENTERBOTTOM)):
                xPoint = ((al - pl) + (aw / 2))
            elif (objectPoint in (uiconst.ANCH_TOPRIGHT,
             uiconst.ANCH_CENTERRIGHT,
             uiconst.ANCH_BOTTOMRIGHT)):
                xPoint = ((al - pl) + aw)
            if (myPoint in (uiconst.ANCH_TOPLEFT,
             uiconst.ANCH_CENTERLEFT,
             uiconst.ANCH_BOTTOMLEFT)):
                self.left = xPoint
            elif (myPoint in (uiconst.ANCH_CENTERTOP,
             uiconst.ANCH_CENTER,
             uiconst.ANCH_CENTERBOTTOM)):
                self.left = (xPoint - (self.width / 2))
            elif (myPoint in (uiconst.ANCH_TOPRIGHT,
             uiconst.ANCH_CENTERRIGHT,
             uiconst.ANCH_BOTTOMRIGHT)):
                self.left = (xPoint - self.width)
            if offset:
                (ox, oy,) = offset
                self.left += ox
                self.top += oy



    def SetAlign(self, align):
        if (align in (uiconst.TOPLEFT, uiconst.RELATIVE)):
            self.align = uiconst.UI_ALNONE
            self.anchors = 0
            self.autoPos = 0
        elif (align == uiconst.ABSOLUTE):
            self.align = uiconst.UI_ALABSOLUTE
            self.anchors = 0
            self.autoPos = 0
        elif (align == uiconst.TOPRIGHT):
            self.align = uiconst.UI_ALNONE
            self.anchors = uiconst.UI_ANCHRIGHT
            self.autoPos = 0
        elif (align == uiconst.BOTTOMLEFT):
            self.align = uiconst.UI_ALNONE
            self.anchors = (uiconst.UI_ANCHBOTTOM | uiconst.UI_ANCHLEFT)
            self.autoPos = 0
        elif (align == uiconst.BOTTOMRIGHT):
            self.align = uiconst.UI_ALNONE
            self.anchors = (uiconst.UI_ANCHBOTTOM | uiconst.UI_ANCHRIGHT)
            self.autoPos = 0
        elif (align == uiconst.TOLEFT):
            self.align = uiconst.UI_ALLEFT
            self.anchors = 0
            self.autoPos = 0
        elif (align == uiconst.TOTOP):
            self.align = uiconst.UI_ALTOP
            self.anchors = 0
            self.autoPos = 0
        elif (align == uiconst.TOBOTTOM):
            self.align = uiconst.UI_ALBOTTOM
            self.anchors = 0
            self.autoPos = 0
        elif (align == uiconst.TORIGHT):
            self.align = uiconst.UI_ALRIGHT
            self.anchors = 0
            self.autoPos = 0
        elif (align == uiconst.TOALL):
            self.align = uiconst.UI_ALCLIENT
            self.anchors = 0
            self.autoPos = 0
        elif (align == uiconst.CENTERLEFT):
            self.align = uiconst.UI_ALNONE
            self.anchors = 0
            self.autoPos = uiconst.AUTOPOSYCENTER
        elif (align == uiconst.CENTERRIGHT):
            self.align = uiconst.UI_ALNONE
            self.autoPos = uiconst.AUTOPOSYCENTER
            self.anchors = uiconst.UI_ANCHRIGHT
        elif (align == uiconst.CENTERTOP):
            self.align = uiconst.UI_ALNONE
            self.autoPos = uiconst.AUTOPOSXCENTER
            self.anchors = 0
        elif (align == uiconst.CENTERBOTTOM):
            self.align = uiconst.UI_ALNONE
            self.autoPos = uiconst.AUTOPOSXCENTER
            self.anchors = uiconst.UI_ANCHBOTTOM
        elif (align == uiconst.CENTER):
            self.align = uiconst.UI_ALNONE
            self.autoPos = uiconst.AUTOPOSCENTER
            self.anchors = 0
        else:
            raise NotImplementedError
        self._setalign = align



    def GetAlign(self):
        return self._setalign



    def Hide(self, *args):
        if (self.state in PICKSTATES):
            self._visibleState = self.state
        self.state = uiconst.UI_HIDDEN



    def Show(self, *args):
        if (self._visibleState is not None):
            self.state = self._visibleState
        else:
            self.state = uiconst.UI_NORMAL



    def Toggle(self, *args):
        if self.IsHidden():
            self.Show()
        else:
            self.Hide()



    def IsHidden(self):
        return bool((self.state == uiconst.UI_HIDDEN))



    def Disable(self, *args):
        if (self._preDisableState is None):
            self._preDisableState = self.state
            self.state = uiconst.UI_DISABLED
            self.opacity = 0.5



    def Enable(self, *args):
        if (self._preDisableState is not None):
            self.state = self._preDisableState
            self._preDisableState = None
        self.opacity = 1.0



    def SetOrder(self, idx):
        uiutil.SetOrder(self, idx)



    def SetAnchors(self, *args):
        return 



    def GetAnchors(self, *args):
        return 



    def FindParentByName(self, parentName):
        object = self
        while getattr(object, 'parent', None):
            if (object.parent.name == parentName):
                return object.parent
            object = object.parent




exports = {'uicls.DecoMetaclass': DecoMetaclass,
 'uicls._GetDecoMetaclassInst': GetDecoMetaclassInst}

